﻿using System;

namespace Zive.Color {

    public static class HslColorConverter {

        //
        // Forward conversion: RGB to XYZ
        //

        public static XyzColor Convert( HslColor inputColor ) {
            return RgbColorConverter.Convert( HslToRgb( inputColor ) );
        }

        public static XyzColor Convert( HslColor inputColor, Illuminants newIlluminant ) {
            return RgbColorConverter.Convert( HslToRgb( inputColor ), newIlluminant );
        }

        //
        // Reverse conversion: XYZ to RGB
        //

        public static HslColor Convert( XyzColor inputColor ) {
            return RgbToHsl( RgbColorConverter.Convert( inputColor ) );
        }

        public static HslColor Convert( XyzColor inputColor, Illuminants newIlluminant ) {
            return RgbToHsl( RgbColorConverter.Convert( inputColor, newIlluminant ) );
        }

        //
        // Implementation
        //

        private static RgbColor HslToRgb( HslColor inputColor ) {
            double h = inputColor.H / 360d;
            double s = inputColor.S / 100d;
            double l = inputColor.L / 100d;

            double m2 = ( l <= 0.5d ) ? ( l * ( s + 1d ) ) : ( l + s - l * s );
            double m1 = l * 2d - m2;
            return new RgbColor(
                inputColor.Illuminant,
                255d * HueToRgb( m1, m2, h + 1d / 3d ),
                255d * HueToRgb( m1, m2, h ),
                255d * HueToRgb( m1, m2, h - 1d / 3d )
            );
        }

        private static double HueToRgb( double m1, double m2, double h ) {
            if ( h < 0d ) {
                h += 1d;
            }
            if ( h > 1d ) {
                h -= 1d;
            }
            if ( h * 6d < 1d ) {
                return m1 + ( m2 - m1 ) * h * 6d;
            }
            if ( h * 2d < 1d ) {
                return m2;
            }
            if ( h * 3d < 2d ) {
                return m1 + ( m2 - m1 ) * ( 2d / 3d - h ) * 6d;
            }
            return m1;
        }

        private static HslColor RgbToHsl( RgbColor inputColor ) {
            var r = inputColor.R / 255d;
            var g = inputColor.G / 255d;
            var b = inputColor.B / 255d;

            var min = Math.Min( Math.Min( r, g ), b );
            var max = Math.Max( Math.Max( r, g ), b );
            var delta = max - min;

            var l = ( max + min ) / 2d;
            double h = 0d;
            double s = 0d;
            if ( !delta.NearlyEqual( 0d ) ) {
                if ( l < 0.5d ) {
                    s = delta / ( max + min );
                } else {
                    s = delta / ( 2d - max - min );
                }

                var deltaR = ( ( ( max - r ) / 6d ) + ( delta / 2d ) ) / delta;
                var deltaG = ( ( ( max - g ) / 6d ) + ( delta / 2d ) ) / delta;
                var deltaB = ( ( ( max - b ) / 6d ) + ( delta / 2d ) ) / delta;

                if ( r.NearlyEqual( max ) ) {
                    h = deltaB - deltaG;
                } else if ( g.NearlyEqual( max ) ) {
                    h = ( 1d / 3d ) + deltaR - deltaB;
                } else if ( b.NearlyEqual( max ) ) {
                    h = ( 2d / 3d ) + deltaG - deltaR;
                }

                if ( h < 0d ) {
                    h += 1d;
                }
                if ( h > 1d ) {
                    h -= 1d;
                }
            }
            return new HslColor( inputColor.Illuminant, h * 360d, s * 100d, l * 100d );
        }

    }

}
